import {
  Injectable,
  ConflictException,
  NotFoundException,
  BadRequestException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import * as bcrypt from 'bcryptjs';
import { User } from './user.entity';
import { Role } from '../roles/role.entity';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';

/**
 * 用户服务类
 * 处理用户管理相关的业务逻辑
 */
@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private userRepository: Repository<User>,
    @InjectRepository(Role)
    private roleRepository: Repository<Role>,
  ) {}

  /**
   * 创建用户
   * @param createUserDto 创建用户数据
   * @returns 创建的用户信息
   */
  async create(createUserDto: CreateUserDto): Promise<User> {
    const { username, email, password, roleIds, ...userData } = createUserDto;

    // 检查用户名是否已存在
    const existingUser = await this.userRepository.findOne({
      where: [{ username }, { email }],
    });

    if (existingUser) {
      throw new ConflictException('用户名或邮箱已存在');
    }

    // 加密密码
    const hashedPassword = await bcrypt.hash(password, 10);

    // 创建用户实例
    const user = this.userRepository.create({
      ...userData,
      username,
      email,
      password: hashedPassword,
    });

    // 处理角色关联
    if (roleIds && roleIds.length > 0) {
      const roles = await this.roleRepository.findByIds(roleIds);
      if (roles.length !== roleIds.length) {
        throw new BadRequestException('部分角色不存在');
      }
      user.roles = roles;
    }

    return this.userRepository.save(user);
  }

  /**
   * 获取用户列表
   * @param page 页码
   * @param limit 每页数量
   * @returns 用户列表和总数
   */
  async findAll(page: number = 1, limit: number = 10) {
    const [users, total] = await this.userRepository.findAndCount({
      relations: ['roles'],
      skip: (page - 1) * limit,
      take: limit,
      order: { createdAt: 'DESC' },
    });

    return {
      users: users.map((user) => this.excludePassword(user)),
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit),
    };
  }

  /**
   * 根据ID获取用户
   * @param id 用户ID
   * @returns 用户信息
   */
  async findOne(id: number): Promise<User> {
    const user = await this.userRepository.findOne({
      where: { id },
      relations: ['roles', 'roles.permissions'],
    });

    if (!user) {
      throw new NotFoundException('用户不存在');
    }

    return this.excludePassword(user);
  }

  /**
   * 更新用户信息
   * @param id 用户ID
   * @param updateUserDto 更新数据
   * @returns 更新后的用户信息
   */
  async update(id: number, updateUserDto: UpdateUserDto): Promise<User> {
    const user = await this.findOne(id);
    const { password, roleIds, ...updateData } = updateUserDto;

    // 如果更新密码，需要加密
    if (password) {
      updateData['password'] = await bcrypt.hash(password, 10);
    }

    // 处理角色更新
    if (roleIds !== undefined) {
      if (roleIds.length > 0) {
        const roles = await this.roleRepository.findByIds(roleIds);
        if (roles.length !== roleIds.length) {
          throw new BadRequestException('部分角色不存在');
        }
        user.roles = roles;
      } else {
        user.roles = [];
      }
    }

    // 更新用户信息
    Object.assign(user, updateData);
    const updatedUser = await this.userRepository.save(user);

    return this.excludePassword(updatedUser);
  }

  /**
   * 删除用户
   * @param id 用户ID
   */
  async remove(id: number): Promise<void> {
    const user = await this.findOne(id);
    await this.userRepository.remove(user);
  }

  /**
   * 切换用户状态
   * @param id 用户ID
   * @returns 更新后的用户信息
   */
  async toggleStatus(id: number): Promise<User> {
    const user = await this.findOne(id);
    user.isActive = !user.isActive;
    const updatedUser = await this.userRepository.save(user);
    return this.excludePassword(updatedUser);
  }

  /**
   * 排除密码字段
   * @param user 用户对象
   * @returns 不包含密码的用户对象
   */
  private excludePassword(user: User): User {
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    const { password, ...userWithoutPassword } = user;
    return userWithoutPassword as User;
  }
}